#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include "Util.hpp"
#include "Log.hpp"

const std::string WEB_ROOT = "wwwroot";
const std::string HOME_PAGE = "index.html";
const std::string PAGE_404 = "404.html";
const std::string PAGE_500 = "500.html";
const std::string HTTP_VERSION = "HTTP/1.1";
const std::string LINE_BREAK = "\n";


enum STATUS_CODE
{
    OK = 200,
    BAD_REQUEST = 400,
    NOT_FOUND = 404,
    SVR_ERROR = 500,
    SVR_UNAVL = 503
};


struct HttpRequest
{
    std::string _request_line;
    std::vector<std::string> _request_headers;
    std::string _blank;
    std::string _request_body;

    std::string _method;
    std::string _uri;
    std::string _version;

    std::string _path;
    std::string _query;
    struct stat _resoucre_stat;
    bool _cgi = false;

    std::unordered_map<std::string, std::string> _header_kvs;
    bool _alive = false;
};

struct HttpResponse
{
    std::string _status_line;
    std::vector<std::string> _response_headers;
    std::string _blank = LINE_BREAK;
    std::string response_body;

    int _status_code = OK;
    int _resource_fd = -1;

    bool _alive = false;
};


// 读取请求，分析请求，构建响应并返回
// 基本IO通信
class EndPoint
{
private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _keep_alive;

private:
    bool RecvHttpRequestLine()
    {
        std::string& line = _http_request._request_line;

        if (Util::ReadLine(_sock, &line) <= 0)
            return false;
        line.pop_back();

        return true;
        // LOG(DEBUG) << _http_request._request_line << std::endl;
    }

    bool RecvHttpRequestHeadler()
    {
        std::string line;
        while (true) // 读到空行，读取结束
        {
            if (Util::ReadLine(_sock, &line) <= 0)
                return false;

            if (line == "\n") {
                _http_request._blank = line; // 将空行放到blank中
                break;
            }

            line.pop_back();
            _http_request._request_headers.push_back(std::move(line));

            line.clear();
        }

        // for (auto& header : _http_request._request_headers)
        //     LOG(DEBUG) << header << std::endl;

        return true;
    }


    void ParseHttpRequestLine()
    {
        auto& method = _http_request._method;

        std::stringstream ss(_http_request._request_line);
        ss >> _http_request._method >> _http_request._uri >> _http_request._version;

        // 处理方法大小写
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);

        // LOG(DEBUG) << _http_request._method << "\n";
        // LOG(DEBUG) << _http_request._uri << "\n";
        // LOG(DEBUG) << _http_request._version << "\n";
    }

    void ParseHttpRequestHeadler()
    {
        for (auto& header : _http_request._request_headers)
            _http_request._header_kvs.insert(Util::GetKV(header, ": "));

        // for (auto& kv : _http_request._header_kvs)
        //     LOG(DEBUG) << kv.first << " : " << kv.second << std::endl;
    }

    bool RecvParseHttpRequestBody()
    {
        auto& body = _http_request._request_body;

        if (_http_request._method == "GET") {
            return true;
        }
        else if (_http_request._method == "POST")
        {
            // 先获取正文长度
            int content_length = 0;

            auto iter = _http_request._header_kvs.find("Content-Length");
            if (iter != _http_request._header_kvs.end())
                content_length = stoi(iter->second);
            else
                LOG(ERROR) << "Content-Length header not existed\n";

            // 读取正文
            char ch = 0;
            while (content_length--)
            {
                if (recv(_sock, &ch, 1, 0) <= 0)
                    return false;

                body.push_back(ch);
            }

            // content_length 已经等于0，说明正文已经读完，
            // 在终端测试我们是回车结束的，因此接下来我们要读取行分隔符，
            if (content_length == 0)
            {
                recv(_sock, &ch, 1, MSG_PEEK | MSG_DONTWAIT);
                if (ch == '\r')
                    recv(_sock, &ch, 1, 0); // \r

                recv(_sock, &ch, 1, MSG_PEEK | MSG_DONTWAIT);
                if (ch == '\n')
                    recv(_sock, &ch, 1, 0); // \n \r\n
            }
        }
        else {
            return false;
        }

        // LOG(DEBUG) << "body: " << body << std::endl;
        return true;
    }

public:
    EndPoint(int sock) : _sock(sock), _keep_alive(false)
    {}
    ~EndPoint()
    {}

    bool RecvHttpRequest()
    {
        return RecvHttpRequestLine() && RecvHttpRequestHeadler();
    }

    bool ParseHttpRequest()
    {
        ParseHttpRequestLine();
        ParseHttpRequestHeadler();
        return RecvParseHttpRequestBody();
    }

    void BuildHttpResponse()
    {
        auto& code = _http_response._status_code;

        auto& path = _http_request._path;
        auto& rsrc_st = _http_request._resoucre_stat;

        // 排除非法请求
        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
            LOG(WARNING) << "bad request invaild method\n";
            code = BAD_REQUEST;
            goto END;
        }

        // 读取是否是HTTP1.1长链接
        if (_http_request._header_kvs.find("Connection") != _http_request._header_kvs.end())
        {
            _keep_alive = true;
            LOG(INFO) << "this request is keep alive\n";
        }

        if (_http_request._method == "GET")
        {
            // 获取uri中的参数和路径
            size_t pos = _http_request._uri.find('?');
            if (pos != std::string::npos)
            {
                Util::CutString(_http_request._uri, &path, &_http_request._query, pos);
                _http_request._cgi = true; // 带参一定用cgi
            }
            else {
                path = _http_request._uri;
            }

            // LOG(DEBUG) << _http_request._path << std::endl;
            // LOG(DEBUG) << _http_request._query << std::endl;

            // 检查资源路径
            path = WEB_ROOT + path; // 拼接web根目录前缀
            if (path.back() == '/')
                path += HOME_PAGE; // 拼接默认访问资源后缀

            // LOG(DEBUG) << path << std::endl;

            //判断资源路径是否合法
            if (stat(path.c_str(), &rsrc_st) == 0)
            {
                if (S_ISDIR(rsrc_st.st_mode)) // 请求资源是目录但不会以/结尾
                {
                    path += "/" + HOME_PAGE;
                    stat(path.c_str(), &rsrc_st);
                }

                if (rsrc_st.st_mode & S_IXUSR || rsrc_st.st_mode & S_IXGRP || rsrc_st.st_mode & S_IXOTH)
                    _http_request._cgi = true;
            }
            else
            {
                LOG(WARNING) << "require " << path + " resource not found\n";
                code = NOT_FOUND;
                goto END;
            }
        }
        else if (_http_request._method == "POST") {
            _http_request._cgi = true;
            path = WEB_ROOT + _http_request._uri;
        }
        else {}


        // 处理请求
        if (_http_request._cgi == true)
        {
            code = ProcessCgi();     // 执行cgi请求，程序运行结果放到response_body中
            if (code == OK)
            {
                LOG(INFO) << "cgi process executed success\n";
                code = ProcessWebPage(); // 讲cgi结果构建网页返回
            }
        }
        else
        {
            code = ProcessWebPage(); // 返回静态网页
            if (code == OK)
                LOG(INFO) << "send " + path + " success\n";
        }


        // 差错处理
    END:
        if (code != OK)
        {
            LOG(INFO) << "headler error begin, code: " << code << '\n';
            ErrorHelper(); // 构建错误响应
        }

        LOG(INFO) << "build response success\n";
    }

private:
    void ErrorHelper()
    {
        _http_request._cgi = false; // 错误处理，返回静态网页
        auto& code = _http_response._status_code;

        switch (code)
        {
        case BAD_REQUEST:
            HeadlerWrong(PAGE_404);
            break;
        case NOT_FOUND:
            HeadlerWrong(PAGE_404); // 单独构建404页面
            break;
        case SVR_ERROR:
            HeadlerWrong(PAGE_404);
            break;
        case SVR_UNAVL:
            HeadlerWrong(PAGE_404);
            break;
        default:
            LOG(WARNING) << "unkown error code" << std::endl;
            break;
        }
    }

    void HeadlerWrong(const std::string& wrong_page)
    {
        _http_request._path = WEB_ROOT + '/' + wrong_page;
        stat(_http_request._path.c_str(), &_http_request._resoucre_stat);
        ProcessWebPage(); // 返回404页面
    }

    int ProcessCgi()
    {
        auto& method = _http_request._method;
        auto& body = _http_request._request_body;
        auto& path = _http_request._path;

        auto& code = _http_response._status_code;

        // 构建两个管道，一个是父写子读，一个是父读子写，管道从父进程角度命名
        // parent output[1]  -->  output[0] child
        // parent  input[0]  <--  input[1]  child
        int input[2];  // 父读子写
        int output[2]; // 父写子读
        if (pipe(input) < 0 || pipe(output) < 0)
        {
            LOG(ERROR) << "failed to create pipes\n";
            return SVR_ERROR;
        }

        pid_t pid = fork();
        if (pid == 0) /* child */
        {
            close(input[0]);
            close(output[1]);

            // 导入环境变量
            int ret = setenv("METHOD", method.c_str(), 1); // 先导请求方法

            if (method == "GET")
                ret |= setenv("QUERY", _http_request._query.c_str(), 0); // 再导GET请求参数
            else if (method == "POST")
                ret |= setenv("CONTENT_LENGTH", std::to_string(body.size()).c_str(), 0); // 再导正文大小

            if (ret < 0)
                LOG(WARNING) << "set env failed, errno: " << errno << '\n';

            // 程序替换之后，子进程就无法确定管道文件的描述符了，故我们做出约定，将子读端重定向至0，写端重定向至1
            dup2(input[1], 1);
            dup2(output[0], 0);

            execl(_http_request._path.c_str(), _http_request._path.c_str(), nullptr);

            std::cerr << "failed to execl " + _http_request._path << std::endl;
            code = SVR_ERROR;
            exit(1);
        }
        else if (pid > 0) /* parent */
        {
            close(input[1]);
            close(output[0]);

            // 向子进程导入数据
            if (_http_request._method == "POST") // 由父进程向管道导入POST请求正文
            {
                auto& body = _http_request._request_body;

                int already = 0;
                while (already < body.size())
                {
                    ssize_t s = write(output[1], body.c_str() + already, body.size() - already);
                    if (s == 0) break;
                    already += s;
                }

                // LOG(DEBUG) << "send request body to subproc: " << body << std::endl;
            }

            // 获取子进程退出结果
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
                // 管道读取子进程输出
                char ch = 0;
                while (read(input[0], &ch, 1))
                    _http_response.response_body.push_back(ch); // 子进程输出放到响应体中

                //判断进程是否正常终止
                if (WIFEXITED(status))
                {
                    LOG(INFO) << "subprocess exited exit code: " << WEXITSTATUS(status) << '\n';
                    if (WEXITSTATUS(status) != 0)
                        code = BAD_REQUEST;
                }
                else
                {
                    LOG(INFO) << "subprocess exited by signal: " << WTERMSIG(status) << '\n';
                    code = BAD_REQUEST;
                }
            }
            else
            {
                LOG(ERROR) << "parent process wait failed\n";
                code = SVR_ERROR;
            }

            close(input[0]);
            close(output[1]);

            return code;
        }
        else /* pid < 0 */
        {
            LOG(ERROR) << "failed to create child process\n";
            return SVR_ERROR;
        }

        return code;
    }

    int ProcessWebPage()
    {
        if (!_http_request._cgi) // 非cgi请求时，提前打开请求网页
        {
            _http_response._resource_fd = open(_http_request._path.c_str(), O_RDONLY);
            if (_http_response._resource_fd < 0)
                return NOT_FOUND;
        }

        auto& line = _http_response._status_line;
        auto& code = _http_response._status_code;
        line = HTTP_VERSION + " " + std::to_string(code) + " " + Util::Code2Desc(code) + LINE_BREAK;

        std::string content_type_header = "Content-Type: ";
        if (_http_request._cgi)
            content_type_header += Util::Suffix2Type(Util::GetSuffix(_http_request._path));
        else
            content_type_header += "text/html";

        std::string content_length_header = "Content-Length: ";
        if (_http_request._cgi)
            content_length_header += std::to_string(_http_response.response_body.size());
        else
            content_length_header += std::to_string(_http_request._resoucre_stat.st_size);

        std::string content_language_header = "Content-Language: zh-cn";

        _http_response._response_headers.push_back(content_type_header);
        _http_response._response_headers.push_back(content_length_header);
        _http_response._response_headers.push_back(content_language_header);

        // for (auto& header : _http_response._response_headers)
        //     LOG(DEBUG) << header << std::endl;

        return OK;
    }

public:
    void SendHttpResponse()
    {
        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);

        for (auto& header : _http_response._response_headers)
        {
            header += LINE_BREAK;
            send(_sock, header.c_str(), header.size(), 0);
        }

        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);

        if (_http_request._cgi)
        {
            auto& body = _http_response.response_body;
            int total = 0;
            while (total < body.size())
            {
                ssize_t s = send(_sock, body.c_str() + total, body.size() - total, 0);
                if (s == 0) break;
                total += s;
            }
        }
        else
        {
            sendfile(_sock, _http_response._resource_fd, 0, _http_request._resoucre_stat.st_size);
            close(_http_response._resource_fd);
        }

        LOG(INFO) << "send http response success\n";
    }

    bool isKeepAlive()
    {
        return _keep_alive;
    }
};


class CallBack
{
public:
    CallBack()
    {}
    ~CallBack()
    {}

public:
    void operator()(int sock)
    {
        // #define DEBUG

#ifdef DEBUG
        while (ep->isKeepAlive())
        {
            std::cout << "get a new link ... " << sock << std::endl;

            char buffer[4096];
            memset(buffer, 0, sizeof(buffer));
            recv(sock, buffer, sizeof(buffer), 0);

            std::cout << "-----------------------------------" << std::endl;
            std::cout << buffer << std::endl;
        }
#else
        EndPoint* ep = new EndPoint(sock);

        // while (ep->isKeepAlive())
        // while (true)
        {
            if (ep->RecvHttpRequest() && ep->ParseHttpRequest())
            {
                LOG(INFO) << "recv and parse request success\n";

                ep->BuildHttpResponse();
                ep->SendHttpResponse();
            }
            else {
                LOG(WARNING) << "recv http request failed\n";
            }
        }

        delete ep;

#endif
    }
};
